package com.czm.test;

import java.util.*;

/**
 * @author CZM
 * @date 2022 06 14 22:21
 */
public class Day01 {

    public static void main(String[] args) {
//        System.out.println(Math.pow(10,2));
//        System.out.println(Math.pow(100,0.5));
//        test001();
//        System.out.println(isSingle(7,14));
//        test002();

        // 将16进制字符串数字转换为10进制数字
//        System.out.println(Integer.parseInt("0001",16));
//        test003();
//        test004();
//        int a = 13;
//        int b = 3;
//        float res = (float) a / (float) b;
//        System.out.println(Math.ceil(res));
//        System.out.println(Math.round((float) 9/5));
//        test005();
//        test006();
//        test007();
//        try (Scanner scanner = new Scanner(System.in)) {
//            String line = scanner.nextLine();
//            int l = scanner.nextInt();
//            int r = scanner.nextInt();
//            solution(line, l, r);
//        }

//        test008();
//        test009();
//        System.out.println(tenTo16(999));
//        System.out.println(intByteSum(999));
//        System.out.println(Integer.parseInt("e7", 16));
//
//        String sss = "12345678";
//        System.out.println(sss.substring(0,2));
//        System.out.println(sss.substring(2,4));
//        System.out.println(sss.substring(4,6));
//        System.out.println(sss.substring(6,8));

       test010();

        // 16进制转10进制
        System.out.println(Integer.parseInt("6ACA", 16));
        // 8进制转10
        System.out.println(Integer.parseInt("12452", 8));
    }

    /**
     * 题目描述：
     如果三个正整数A、B、C ,A²+B²=C²则为勾股数
     如果ABC之间两两互质，即A与B，A与C，B与C均互质没有公约数，
     则称其为勾股数元组。
     请求出给定n~m范围内所有的勾股数元组

     输入描述
     起始范围
     1 < n < 10000
     n < m < 10000

     输出描述
     ABC保证A<B<C
     输出格式A B C
     多组勾股数元组，按照A B C升序的排序方式输出。
     若给定范围内，找不到勾股数元组时，输出Na。

     示例一
     输入
     1
     20
     输出
     3 4 5
     5 12 13
     8 15 17
     示例二
     输入
     5
     10
     输出
     Na
     */
    // 解题思路：双层for循环，求出所有勾股数元组，在筛选出符合条件的
    // 勾股数元组
    public static void test001() {
        Scanner sc = new Scanner(System.in);
        int num1 = sc.nextInt();
        int num2 = sc.nextInt();
        boolean flag = true;
        for (int i = num1; i < num2; i++) {
            for (int j = i + 1; j <= num2; j++) {
                // pow()：取几次方，0.5次方相当于开根号
                double res = Math.pow(i, 2) + Math.pow(j, 2);
                double pow = Math.pow(res, 0.5);
                // 结果小于nums且是整数    判断一个double型数据是不是整数：Math.abs(pow - Math.round(pow)) < Double.MIN_VALUE
                if (pow <= num2 && Math.abs(pow - Math.round(pow)) < Double.MIN_VALUE) {
                    int powRes = (int) pow;
                    // 两两互质
                    if (isSingle(i, j) && isSingle(i, powRes) && isSingle(j, powRes)) {
                        System.out.println(i + " " + j + " " + powRes);
                        flag = false;
                    }
                }
            }
        }
        if (flag) {
            System.out.println("Na");
        }
    }

    /**
     * 判断两个数字是否互质 （没有相同的公约数）
     *
     * @param num1
     * @param num2
     * @return
     */
    private static boolean isSingle(int num1, int num2) {
        int res = Math.min(num1, num2);
        for (int i = 2; i <= res; i++) {
            if (num1 % i == 0 && num2 % i == 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 题目描述：
     给定两个整数数组，arr1、arr2，数组元素按升序排列；
     假设从arr1、arr2中分别取出一个元素，可构成一对元素；
     现在需要取出k对元素，并对取出的所有元素求和，计算和的最小值；
     注意：两对元素对应arr1、arr2的下标是相同的，视为同一对元素。

     输入描述
     输入两行数组arr1、arr2
     每行首个数字为数组大小size， 0 < size <= 100
     arr1，arr2中的每个元素e， 0< e <1000
     接下来一行，正整数k 0 < k <= arr1.size * arr2.size

     输出描述
     满足要求的最小值

     示例一
     输入

     1 1 2
     1 2 3
     2
     输出
     4
     说明：
     用例中需要取两个元素，取第一个数组第0个元素与第二个数组第0个元素组成一个元素[1,1];
     取第一个数组第1个元素与第二个数组第0个元素组成一个元素[1,1];
     求和为1+1+1+1=4 ,满足要求最小
     */
    // 指定元素对最小和
    // 思路：双层for循环，求出两个数组所有值一一相加的值，放进另一个数组中，并进行升序排序，求出指定前几个的和
    public static void test002() {
        Scanner sc = new Scanner(System.in);
        String line1 = sc.nextLine();
        String line2 = sc.nextLine();
        int[] array1 = stringToArray(line1);
        int[] array2 = stringToArray(line2);
        int len1 = array1.length;
        int len2 = array2.length;
        int groupCount = sc.nextInt();
        List<Integer> list = new ArrayList();
        // 双层for循环，求出两个数组所有值一一相加的值，放进另一个数组中
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                list.add(array1[i] + array2[j]);
            }
        }
        // 对集合进行排序（升序）
        Collections.sort(list);
        int res = 0;
        // 求出指定前几个的和
        for (int i = 0; i < groupCount; i++) {
            res += list.get(i);
        }
        System.out.println(res);
    }

    private static int[] stringToArray(String line) {
        String[] strings = line.split(" ");
        int[] array = new int[strings.length];
        for (int i = 0; i < strings.length; i++) {
            array[i] = Integer.parseInt(strings[i]);
        }
        return array;
    }
    // 32 01 00 AE
    // 90 02 00 01 02
    // 30 03 00 AB 32 31
    // 31 02 00 32 33
    // 33 01 00 CC

    /**
     * 题目描述：
     TLV编码是按TagLengthValue格式进行编码的，一段码流中的信元用tag标识，tag在码流中唯一不重复，
     length表示信元value的长度，value表示信元的值，码流以某信元的tag开头，tag固定占一个字节，length固定占两个字节，字节序为小端序，
     现给定tlv格式编码的码流以及需要解码的信元tag，请输出该信元的value。

     输入码流的16进制字符中，不包括小写字母；
     且要求输出的16进制字符串中也不要包含小写字母；
     码流字符串的最大长度不超过50000个字节。

     输入描述
     第一行为第一个字符串 ，表示待解码信元的tag；
     输入第二行为一个字符串， 表示待解码的16进制码流；
     字节之间用空格分割。

     输出描述
     输出一个字符串，表示待解码信元以16进制表示的value。

     示例一
     输入
     31
     32 01 00 AE 90 02 00 01 02 30 03 00 AB 32 31 31 02 00 32 33 33 01 00 CC
     输出
     32 33
     说明
     需要解析的信源的tag是31；
     从码流的起始处开始匹配，tag为32的信元长度为1(01 00,小端序表示为1)；
     第二个信元的tag为90 其长度为2；
     第三个信元的tag为30 其长度为3；
     第四个信元的tag为31 其长度为2(02 00)；
     所以返回长度后面的两个字节即可 为 32 33。
     */
    // 输出信元的值
    public static void test003() {
        Scanner sc = new Scanner(System.in);
        String tag = sc.nextLine();
        String line = sc.nextLine();
        int len = line.length();
        int p = 0;
        while (p < len) {
            // value的长度  后两位拼接前两位，16进制，需要转换为10进制
            int length = Integer.parseInt(line.substring(p + 6, p + 8) + line.substring(p + 3, p + 5), 16);
            // 找到目标值
            if (tag.equals(line.substring(p, p + 2))) {
                String value = line.substring(p + 9, p + 9 + (length * 3 - 1));
                System.out.println(value);
                return;
            }
            // 动态算出p应该移动几位，tag和length是固定的，value根据length决定，要注意空格
            p += 9 + length * 3;
        }
    }

    /**
     * 题目描述：
     一天一只顽猴想要从山脚爬到山顶
     途中经过一个有n个台阶的阶梯，但是这个猴子有个习惯，每一次只跳1步或3步
     试问？猴子通过这个阶梯有多少种不同的跳跃方式

     输入描述
     输入只有一个这个数n 0 < n < 50
     此阶梯有多个台阶

     输出描述
     有多少种跳跃方式

     示例一
     输入
     50
     输出
     122106097
     示例二
     输入
     3
     COPY
     输出
     2
     */
    /**
     * 解题思路：
     * 动态规划题，找规律
     * n = 1  1
     * n = 2  1
     * n = 3  2
     * n = 4  3
     * n = 5  4
     * n = 6  6
     * n = 7  9
     * 可得当n > 3时 f(n) = f(n - 1) + f(n - 3)
     * 所以可以用递归，n = 1/2/3 时值是固定的，为递归结束条件
     */
    // 猴子爬山
    public static void test004() {
        Scanner sc = new Scanner(System.in);
        int nums = sc.nextInt();
        int value = fun(nums);
        System.out.println(value);
    }

    public static int test004_(int num){
        if (num == 1) {
            return 1;
        }
        if (num == 2) {
            return 1;
        }
        if (num == 3) {
            return 2;
        }
        return test004_(num - 1) + test004_(num - 3);
    }

    private static int fun(int nums) {
        // 递归结束条件
        if (nums == 1 || nums == 2) {
            return 1;
        }
        // 递归结束条件
        if (nums == 3) {
            return 2;
        }
        // 规律
        return fun(nums - 1) + fun(nums - 3);
    }

    /**
     * 题目描述：
     为了充分发挥Gpu算力，
     需要尽可能多的将任务交给GPU执行，
     现在有一个任务数组，
     数组元素表示在这1s内新增的任务个数，
     且每秒都有新增任务，
     假设GPU最多一次执行n个任务，
     一次执行耗时1s，
     在保证Gpu不空闲的情况下，最少需要多长时间执行完成。

     输入描述
     第一个参数为gpu最多执行的任务个数
     取值范围1~10000
     第二个参数为任务数组的长度
     取值范围1~10000
     第三个参数为任务数组
     数字范围1~10000

     输出描述
     执行完所有任务需要多少秒

     示例一
     输入
     3
     5
     1 2 3 4 5
     输出
     6
     说明
     一次最多执行3个任务，最少耗时6s

     示例二
     输入
     4
     5
     5 4 1 1 1
     输出
     5
     说明
     一次最多执行4个任务，最少耗时5s
     */
    // CPU最少执行时间
    public static void test005() {
        Scanner sc = new Scanner(System.in);
        // 每秒执行任务数
        int qps = Integer.parseInt(sc.nextLine());
        int arrayLen = Integer.parseInt(sc.nextLine());
        String line = sc.nextLine();
        int[] array = new int[arrayLen];
        String[] strings = line.split(" ");
        for (int i = 0; i < arrayLen; i++) {
            array[i] = Integer.parseInt(strings[i]);
        }
        // 使用的时间
        int time = 0;
        // 剩余任务，初始为0
        int leaveTask = 0;
        for (int i = 0; i < arrayLen; i++) {
            // 若当前任务+剩余任务大于每秒执行任务，则剩余任务 = 之前剩余任务 + 当前任务 - 每秒执行任务
            if (array[i] + leaveTask > qps) {
                leaveTask = array[i] + leaveTask - qps;
            } else {
                leaveTask = 0;
            }
            // 累加时间
            time++;
        }
        // 当数组走完后还有剩余任务，用剩余任务/每秒执行任务算出需要的时间
        if (leaveTask > 0) {
            int res = leaveTask / qps;
            time = time + res;
            if (leaveTask % qps != 0) {
                time++;
            }
        }
        System.out.println(time);
    }

    /**
     * 题目描述：
     小明今年升学到了小学一年级，
     来到新班级后，发现其他小朋友身高参差不齐，
     然后就想基于各小朋友和自己的身高差，对他们进行排序，
     请帮他实现排序

     输入描述
     第一行为正整数 H和N
     0 < H < 200 为小明的身高
     0 < H < 50 为新班级其他小朋友个数
     第二行为N个正整数
     H1 ~ Hn分别是其他小朋友的身高
     取值范围0 < Hi < 200
     且N个正整数各不相同

     输出描述
     输出排序结果，各正整数以空格分割
     和小明身高差绝对值最小的小朋友排在前面
     和小明身高差绝对值最大的小朋友排在后面
     如果两个小朋友和小明身高差一样
     则个子较小的小朋友排在前面

     示例一
     输入
     100 10
     95 96 97 98 99 101 102 103 104 105
     输出
     99 101 98 102 97 103 96 104 95 105
     */
    // 小朋友身高排序
    public static void test006() {
        Scanner sc = new Scanner(System.in);
        String line1 = sc.nextLine();
        String line2 = sc.nextLine();
        String[] input1 = line1.split(" ");
        String[] input2 = line2.split(" ");
        // 基准数
        int height = Integer.parseInt(input1[0]);
        // 人数
        int num = Integer.parseInt(input1[1]);
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(Integer.parseInt(input2[i]));
        }

        // list.sort() 自定义排序规则
        // 在 list.sort() 底层代码中，以 compare(list [ i - 1 ], list [ i ]) 形式调用该方法，
        // 当其的返回值大于0时，list [ i - 1 ] 将与 list [ i ] 交换位置
        list.sort((h1, h2) -> {
            // 与基准数的绝对值
            int diff1 = Math.abs(h1 - height);
            int diff2 = Math.abs(h2 - height);
            // 若与基准数的绝对值相等，则高的排在后，否则基准数大的排在后
            return diff1 == diff2 ? h1 - h2 : diff1 - diff2;
        });
        for (Integer h : list) {
            System.out.print(h + " ");
        }
    }

    /**
     * 输入描述
     使用换行隔开3个参数
     第一个参数为文章内容 即英文字符串
     第二个参数为翻转起始单词下标，下标从0开始
     第三个参数为结束单词下标

     输出描述
     翻转后英文文章片段每个单词之间以一个半角空格分割输出

     示例一
     输入
     I am a developer.
     0
     3
     输出
     developer. a am I
     示例二
     输入
     hello world!
     0
     3
     输出
     world! hello
     */
    // 字符串翻转
    public static void test007() {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        int start = sc.nextInt();
        int end = sc.nextInt();
        String[] strings = line.split(" ");
        // 如果翻转起始数大于或是最后一个，则原样输出
        if(start >= strings.length - 1) {
            System.out.println(line);
            return;
        }
        // 如果翻转起始数小于0，则当0处理
        if (start < 0) {
            start = 0;
        }
        // 如果翻转结束数大于等于数组长度，则当数组长度-1
        if (end >= strings.length) {
            end = strings.length - 1;
        }
        // 互换位置，直到重合或超过
        while (start < end){
            String temp = strings[start];
            strings[start] = strings[end];
            strings[end] = temp;
            start++;
            end--;
        }
        for (String s : strings) {
            System.out.print(s + " ");
        }
    }

    private static void solution(String line, int l, int r) {
        String[] words = line.trim().split(" ");
        if (r > words.length - 1) r = words.length - 1;
        if (words.length == 0 ||
                l < 0 ||
                r - l <= 0) {
            System.out.println("EMPTY");
            return;
        }

        while (l < r) {
            String tmp = words[l];
            words[l] = words[r];
            words[r] = tmp;
            l++;
            r--;
        }

        for (int i = 0; i < words.length; i++) {
            System.out.print(words[i]);
            if (i != words.length - 1) {
                System.out.print(" ");
            }
        }
    }

    /**
     * 双11众多商品进行打折销售，小明想购买一些自己心仪的商品，
     * 但由于受购买资金限制，所以他决定从众多心意商品中购买3件，
     * 而且想尽可能的花完资金，
     * 现在请你设计一个程序帮助小明计算尽可能花费的最大资金额。
     */
    public static void test008() {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        int count = sc.nextInt();
        String[] split = line.split(",");
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < split.length; i++) {
            list.add(Integer.parseInt(split[i]));
        }
        Collections.sort(list);
        int res = -1;
        if (list.size() >= 3) {
            for (int i = 0; i < list.size() - 2; i++) {
                if (list.get(i) > count) {
                    break;
                }
                for (int j = i + 1; j < list.size() - 1; j++) {
                    if (list.get(i) + list.get(j) > count) {
                        break;
                    }
                    for (int k = j + 1; k < list.size(); k++) {
                        int all = list.get(i) + list.get(j) + list.get(k);
                        if (all <= count) {
                            res = Math.max(res, all);
                        } else {
                            break;
                        }
                    }
                }
            }
        }
        System.out.println(res);
    }

    /**
     * 题目描述：
     * 给定两个字符串，
     * 从字符串2中找出字符串1中的所有字符，
     * 去重并按照ASCII码值从小到大排列，
     * <p>
     * 输入描述
     * 字符范围满足ASCII编码要求，
     * 输入字符串1长度不超过1024，
     * 字符串2长度不超过100
     * <p>
     * 输出描述
     * 按照ASCII由小到大排序
     * <p>
     * 示例一
     * 输入
     * <p>
     * bach
     * bbaaccddfg
     * COPY
     * 输出
     * <p>
     * abc
     * COPY
     * 示例二
     * 输入
     * <p>
     * fach
     * bbaaccedfg
     * COPY
     * 输出
     * <p>
     * acf
     */
    // 寻找重复字符
    public static void test009() {
        Scanner scanner = new Scanner(System.in);
        String line1 = scanner.nextLine();
        String line2 = scanner.nextLine();
        List<Character> list = new ArrayList<>();
        // 将输入的字符串转为字符数组
        for (int i = 0; i < line2.length(); i++) {
            list.add(line2.charAt(i));
        }
        // 用来存第一第二次输入都有的字符
        List<Character> resList = new ArrayList<>();
        for (int i = 0; i < line1.length(); i++) {
            char c = line1.charAt(i);
            // &&后的条件是防止重复
            if (list.contains(c) && !resList.contains(c)) {
                resList.add(c);
            }
        }
        // 对集合进行排序，默认是按ASCII码值排序
        Collections.sort(resList);
        for (Character c : resList) {
            System.out.print(c);
        }
    }

    /**
     * 题目描述：
     对一个数据a进行分类，
     分类方法是，此数据a(4个字节大小)的4个字节相加对一个给定值b取模，
     如果得到的结果小于一个给定的值c则数据a为有效类型，其类型为取模的值。
     如果得到的结果大于或者等于c则数据a为无效类型。

     比如一个数据a=0x01010101，b=3
     按照分类方法计算：(0x01+0x01+0x01+0x01)%3=1
     所以如果c等于2，则此a就是有效类型，其类型为1
     如果c等于1，则此a是无效类型

     又比如一个数据a=0x01010103，b=3
     按分类方法计算：(0x01+0x01+0x01+0x03)%3=0
     所以如果c=2则此a就是有效类型 其类型为0
     如果c等于0 则此a是无效类型

     输入12个数据，
     第一个数据为c，第二个数据为b，
     剩余10个数据为需要分类的数据

     请找到有效类型中包含数据最多的类型，
     并输出该类型含有多少个数据

     输入描述
     输入12个数据用空格分割，
     第一个数据为c，第二个数据为b，
     剩余10个数据为需要分类的数据。

     输出描述
     请找到有效类型中包含数据最多的类型，
     并输出该类型含有多少个数据。

     示例一
     输入
     3 4 256 257 258 259 260 261 262 263 264 265
     输出
     3
     说明
     这10个数据4个字节相加后的结果分别是
     1 2 3 4 5 6 7 8 9 10
     故对4取模的结果为
     1 2 3 0 1 2 3 0 1 2
     c是3所以012都是有效类型
     类型为1和2的有3个数据
     类型为0和3的只有两个
     示例二
     输入
     1 4 256 257 258 259 260 261 262 263 264 265
     输出
     2
     */
    // ps：这道题我的理解是把输入的转为16进制，从后往前取依次取2位，如果16进制数长度大于8，取后8位，然后转为10进制相加，与基数取模。它的案列给得不清楚，不确定题目是不是这个意思。
    // 输出最多类型的个数
    public static void test010() {
        Scanner sc = new Scanner(System.in);
        List<Integer> list = new ArrayList<>();
        String s = sc.nextLine();
        String[] strings = s.split(" ");
        // 转为int型集合
        for (int i = 0; i < strings.length; i++) {
            list.add(Integer.parseInt(strings[i]));
        }
        // map用来存储出现次数，key为取模后的值，value为次数
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 2; i < list.size(); i++) {
            // 将输入的数转为16进制
            String num16 = tenTo16(list.get(i));
            int len = num16.length();
            // 累加的和
            int allRes = 0;
            // 从后往前取依次取2位，如果16进制数长度大于8，取后8位，然后转为10进制相加
            if (len >= 8) {
                // 16进制转10进制：Integer.parseInt("ABCD", 16);
                allRes = Integer.parseInt(num16.substring(len - 8, len - 6), 16) + Integer.parseInt(num16.substring(len - 6, len - 4), 16)
                        + Integer.parseInt(num16.substring(len - 4, len - 2), 16) + Integer.parseInt(num16.substring(len - 2, len), 16);
            } else if (len == 7) {
                allRes = Integer.parseInt(num16.substring(0, 1), 16) + Integer.parseInt(num16.substring(1, 3), 16)
                        + Integer.parseInt(num16.substring(3, 5), 16) + Integer.parseInt(num16.substring(5, 7), 16);
            } else if (len == 6) {
                allRes = Integer.parseInt(num16.substring(0, 2), 16) + Integer.parseInt(num16.substring(2, 4), 16)
                        + Integer.parseInt(num16.substring(4, 6), 16);
            } else if (len == 5) {
                allRes = Integer.parseInt(num16.substring(0, 1), 16) + Integer.parseInt(num16.substring(1, 3), 16)
                        + Integer.parseInt(num16.substring(3, 5), 16);
            } else if (len == 4) {
                allRes = Integer.parseInt(num16.substring(0, 2), 16) + Integer.parseInt(num16.substring(2, 4), 16);
            } else if (len == 3) {
                allRes = Integer.parseInt(num16.substring(0, 1), 16) + Integer.parseInt(num16.substring(1, 3), 16);
            } else if (len == 2) {
                allRes = Integer.parseInt(num16.substring(0, 2), 16);
            } else if (len == 1) {
                allRes = Integer.parseInt(num16.substring(0, 1), 16);
            }
            System.out.println(allRes);
            int modeValue = allRes % list.get(1);
            System.out.println(modeValue);
            // 用map存次数
            if (modeValue < list.get(0)) {
                if (map.containsKey(modeValue)) {
                    map.put(modeValue, map.get(modeValue) + 1);
                } else {
                    map.put(modeValue, 1);
                }
            }
        }
        int maxCount = 0;
        // 找出最大的次数
        for (Integer key : map.keySet()) {
            maxCount = Math.max(map.get(key), maxCount);
        }
        System.out.println(maxCount);
    }

    /**
     * 十进制转十六进制
     *
     * @return
     */
    private static String tenTo16(int num) {
        String s = "";
        do {
            int res = num % 16;
            num = num / 16;
            if (res == 15) {
                s = "F" + s;
            } else if (res == 14) {
                s = "E" + s;
            } else if (res == 13) {
                s = "D" + s;
            } else if (res == 12) {
                s = "C" + s;
            } else if (res == 11) {
                s = "B" + s;
            } else if (res == 10) {
                s = "A" + s;
            } else {
                s = res + s;
            }
        } while (num != 0);
        return s;
    }

    private static int intByteSum(int x) {
        int sum = 0;
        for (int i = 0; i < 4; i++) {
            sum += (byte) (x >> (i * 8));
        }
        return sum;
    }

}
